#include "ConnMgr.hpp"
#include "Device.hpp"
#include "KieperException.hpp"
#include "ExceptionUtil.hpp"
#include "Message.hpp"
#include "Packet.hpp"
#include "IncomingPacket.hpp"

namespace kieper {
    using boost::mutex;
    using boost::condition_variable;
    using std::auto_ptr;

    static mutex msgIdMutex;
    
    ConnMgr::ConnMgr() : isInitialized_(false), packetProcessor_(*this) {}
    ConnMgr::~ConnMgr() {}

    void ConnMgr::initialize() {
        if (isInitialized_) {
            THROW(KieperException, "Connection manager already initialized");
        }
        doInitialize();
        isInitialized_ = true;
    }

    DeviceMapPtr ConnMgr::findDevices() {
        return doFindDevices();
    }
    
    void ConnMgr::send(const Device& device, const Message& message) {
        message.getPacket().setPacketId(getNextMsgId());
        message.getPacket().setDeviceId(0);
        doSend(device, message.getPacket());
    }

    void ConnMgr::putResponsePacket(uint32_t packetId, IncomingPacket* packet) {
        packets_[packetId] = packet;
    }

    void ConnMgr::putCondition(uint32_t packetId, condition_variable* condition) {
        conditions_[packetId] = condition;
    }

    void ConnMgr::putMutex(uint32_t packetId, mutex* mtx) {
        mutexes_[packetId] = mtx;
    }


    IncomingPacket* ConnMgr::getNextPacket() {
        return doGetNextPacket();
    }

    bool ConnMgr::responseArrived(uint32_t packetId) {
        return packets_.find(packetId) != packets_.end();
    }

    bool ConnMgr::isThreadWaitingForResponse(uint32_t packetId) {
        return conditions_.find(packetId) != conditions_.end();
    }

    auto_ptr<IncomingPacket> ConnMgr::getResponse(uint32_t packetId) {
        Packets::iterator it = packets_.find(packetId);
        auto_ptr<IncomingPacket> packet(it->second);
        packets_.erase(it);
        return packet;
    }

    condition_variable* ConnMgr::getCondition(uint32_t packetId) {
        return conditions_[packetId];
    }

    mutex* ConnMgr::getMutex(uint32_t packetId) {
        return mutexes_[packetId];
    }

    uint32_t ConnMgr::getNextMsgId() {
        uint32_t nextMsgIdToRet;
        msgIdMutex.lock();
        nextMsgIdToRet = nextMsgId_;
        ++nextMsgId_;
        msgIdMutex.unlock();
        return nextMsgIdToRet;
    }

    void ConnMgr::startPacketProcessor() {
        packetProcessor_.start();
    }

    bool ConnMgr::isPacketProcessorReady() {
        return packetProcessor_.isReady();
    }
}
